#pragma once 
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>
#include <unistd.h>
#include <string>

#include "ConnetInfo.hpp"
#include "Tool.hpp"
#include "UserManager.hpp"
#include "MsgPool.hpp"

using namespace std;

#define PTHREAD_COUNT 1 
#define MAX_ROUND 10

class TcpConnet
{
    public:
        TcpConnet()
        {
            new_sock_ = -1;
            server_ = NULL;
        }
        ~TcpConnet()
        {

        }
        void SetSock(int newsock)
        {
            new_sock_ = newsock;
        }
        int GetSock()
        {
            return new_sock_;
        }
        void SetServer(void* server)
        {
            server_ = server;
        }
        void* GetSever()
        {
            return server_;
        }
    private:
       int  new_sock_;
       void* server_;
};

class ChatServer
{
    public:
        ChatServer()
        {
            tcp_sock_ = -1;
            tcp_port_ = TCP_PORT;
            udp_sock_ = -1;
            udp_port_ = UDP_PROT;
            um_ = NULL;
            memset(pthread_pro_,0,PTHREAD_COUNT*sizeof(pthread_t));
            memset(pthread_con_,0,PTHREAD_COUNT*sizeof(pthread_t));
            msg_pool_ = NULL;
            udp_msg_ = NULL;
        }
        ~ChatServer()
        {
            if(um_)
            {
                delete um_; 
                um_ = NULL;
            }
            if(msg_pool_)
            {
                delete msg_pool_;
                msg_pool_ = NULL;
            }
            if(udp_msg_)
            {
                delete udp_msg_;
                udp_msg_ = NULL;
            }
        }
        int Init()
        {
            msg_pool_ = new MsgPool(1024);
            //创建套接字
            tcp_sock_ = socket(AF_INET,SOCK_STREAM,0);
            if(tcp_sock_ < 0)
            {
                perror("socket()");
                return -1;
            }
            //绑定
            struct sockaddr_in addr;
            addr.sin_family = AF_INET;
            addr.sin_port = htons(TCP_PORT);
            addr.sin_addr.s_addr = inet_addr("0.0.0.0");
            int ret = bind(tcp_sock_,(struct sockaddr*)&addr,sizeof(addr));
            if(ret < 0)
            {
                perror("bind");
                return -1;
            }
            //监听
            ret = listen(tcp_sock_,5);
            if(ret < 0)
            {
                perror("listen");
                return -1;
            }
            //开辟用户管理的动态空间
            um_ = new UserManager();

            //创建udp套接字 
            udp_sock_ = socket(AF_INET,SOCK_DGRAM,0);
            if(udp_sock_ < 0)
            {
                LOG(ERROR,"udp_socket faild") << endl;
                return -1;
            }
            //连接
            struct sockaddr_in udp_addr;
            udp_addr.sin_family = AF_INET;
            udp_addr.sin_port = htons(udp_port_);
            udp_addr.sin_addr.s_addr = inet_addr("0.0.0.0");
            ret = bind(udp_sock_,(struct sockaddr*)&udp_addr,sizeof(udp_addr));
            if(ret < 0)
            {
                LOG(ERROR,"bind faild") << endl;
                return -1;
            }

            return 0;
        }
        int Start()
        {
            //udp创建两个线程
            //1.生产线程---收到数据，转到消息池里
            //2.消费线程---发送数据，从消息池里发送给在线用户（在线用户是指状态在线，且有udp的地址）
            for(int i = 0;i < PTHREAD_COUNT;i++)
            {
                //创建两个生产线程，两个消费线程
                //生产线程--接收数据至消息池ProductStart
                int ret = pthread_create(&pthread_pro_[i],NULL,ProductStart,(void*)this);
                if(ret < 0)
                {
                    LOG(ERROR,"pthread_create ProductStart faild") << endl;
                    return -1;
                }
                ret = pthread_create(&pthread_con_[i],NULL,ConsumeStart,(void*)this);
                if(ret < 0)
                {
                    LOG(ERROR,"pthread_create ConsumeStart faild") << endl;
                    return -1;
                }
            }

            //accept创建新连接，专门为客户端处理登录和注册
            struct sockaddr_in addr;
            socklen_t len = sizeof(addr);
            while(1)
            {
                int new_sock = accept(tcp_sock_,(struct sockaddr*)&addr,&len);
                if(new_sock < 0)
                {
                    perror("accept");
                    continue;
                }
                TcpConnet* tc = new TcpConnet();
                tc->SetSock(new_sock);
                tc->SetServer((void*)this);
                pthread_t pid;
                int ret = pthread_create(&pid,NULL,DealLoginRegister,(void*)tc);
                if(ret < 0)
                {
                    perror("pthread_create");
                    close(new_sock);
                    delete(tc);
                    continue;
                }
            }
        }
        //udp收发的两个线程
    private:
        static void* ProductStart(void* arg)
        {
            pthread_detach(pthread_self());
            ChatServer* cs = (ChatServer*)arg;    
            //接收数据
            while(1)
            {
                cs->RecvMsg();
                sleep(1);
            }
            
        }
        static void* ConsumeStart(void* arg)
        {
            pthread_detach(pthread_self());
            ChatServer* cs = (ChatServer*)arg;    
            //发送数据
            while(1)
            {
                cs->SendMsg();
                sleep(1);
            }
        }
    public:
        int RecvMsg()
        {
            
            char buf[UDP_COUNT] = {0};
            struct sockaddr_in addr;
            socklen_t len = sizeof(addr);
            ssize_t  recvfrom_size = recvfrom(udp_sock_,buf,sizeof(buf)-1,0,(struct sockaddr*)&addr,&len);
            if(recvfrom_size < 0)
            {
                LOG(ERROR,"recvfrom faild") << endl;
                return -1;
            }
            else if(recvfrom_size == 0)
            {
                //对端关闭
                close(udp_sock_);
                return -1;
            }
            
            //收到的二进制序列化为对象
            string msg;
            msg.assign(buf,strlen(buf));
            UdpMsg um;  
            //此时客户端的名字，学校，密码，消息我们都知道了
            um.DeSerialize(msg);
            //确认该用户是否在线
            if(!(um_->IsLogin(um.user_id_,addr,len)))
            {
                //没找到
                LOG(ERROR,"user_id can not found") << endl;
                return -1;
            }
            msg_pool_->RecvMsgToPool(msg);
            return 0;
        }

        int SendMsg()
        {
            //从消息池里拿消息--->发送给所有的在线用户
            string msg;
            msg_pool_->SendMsgToClient(&msg);
            //发送信息
            //在线用户列表
            vector<UserInfo> Online_user_;
            Online_user_ = um_->GetOnlineUser();
            
            for(size_t i = 0; i < Online_user_.size() ; i++)
            {
                ssize_t sendto_size = sendto(udp_sock_,msg.c_str(),msg.size(),0,(struct sockaddr*)Online_user_[i].GetAddr(),Online_user_[i].GetAddrLen());
                if(sendto_size < 0)
                {
                    LOG(ERROR,"sendto faild") << endl;
                    return -1;
                }
                //发送成功
                cout << i << ":" << msg << "--->" << inet_ntoa(Online_user_[i].GetAddr()->sin_addr) << endl;
            }
           return 0; 
        }



    private:
        //内部调用，放在私有部分
        static void* DealLoginRegister(void* arg)
        {
           //处理注册和登录的接口
           //先收到第一个tcp包 判断它是注册还是登录
           //再收到第二个tcp包，是它的约定的数据
           TcpConnet* tc = (TcpConnet*)arg;
           ChatServer* cs = (ChatServer*)tc->GetSever();
           //一个字符接收是注册还是登录 
           char resq = 1;
           ssize_t recv_size = recv(tc->GetSock(),&resq,1,0);
           if(recv_size < 0)
           {
               //连接出错
               close(tc->GetSock());
               delete tc;
               return NULL;
           }
           else if(recv_size == 0)
           {
               //对端连接关闭
               close(tc->GetSock());
               delete tc;
               return NULL;
           }
           //正常接收一个字节后，判断
           ReplyInfo reply;
           switch(resq)
           {
               case Register:
                   {
                        //处理注册
                        reply.resp_state = cs->DealRegister(tc->GetSock(),&reply.id_);
                        break;
                   }
               case Login:
                   {
                        //处理登录
                        reply.resp_state = cs->DealLogin(tc->GetSock(),&reply.id_);

                        break;
                   }
           }
           LOG(INFO,"resp_state:")<< resq_type[reply.resp_state] << endl;
           LOG(INFO,"resq_id_:") << reply.id_ << endl;
           int max_round = MAX_ROUND;
           while(max_round--)
           {
              ssize_t send_size = send(tc->GetSock(),&reply,sizeof(reply),0);
              if(send_size >= 0)
              {
                 break;
              }
                LOG(INFO,"send faild") << endl;
           }
           close(tc->GetSock());
           delete tc;
           tc = NULL;
           return NULL; 
        }

        int DealRegister(int newsock,uint32_t* user_id)
        {
            struct RegisterInfo ri;
            ssize_t recv_size = recv(newsock,&ri,sizeof(ri),0);
            if(recv_size < 0)
            {
                //连接异常，关闭新连接套接字
                close(newsock);
                return -1;
            }
            else if(recv_size == 0)
            {
                //对端关闭套接字
                close(newsock);
                return -1;
            }
            //正常收到
            //把收到的数据添加到用户管理模块，进行保存
            
            int ret = um_->DealRegister(ri.name,ri.school,ri.passwd,user_id);
            if(ret < 0)
            {
                return Register_Faild;
            }
            return Register_Success;
        }

        int DealLogin(int newsock,uint32_t* id)
        {
            LoginInfo li;
            ssize_t recv_size = recv(newsock,&li,sizeof(li),0);
            if(recv_size < 0)
            {
                close(newsock);
                return -1;
            }
            else if(recv_size == 0)
            {
                close(newsock);
                return -1;
            }
            *id = li.id_;
            //正常接收到 调用用户管理
            int ret = um_->DealLogin(li.id_,li.passwd);
            if(ret < 0)
            {
                return Login_Faild; 
            }
            return Login_Success;
        }
        
    private:
        int tcp_sock_;
        int udp_sock_;
        int tcp_port_;
        int udp_port_;
        UserManager* um_; 
        pthread_t pthread_pro_[PTHREAD_COUNT];
        pthread_t pthread_con_[PTHREAD_COUNT];
        MsgPool* msg_pool_;        
        UdpMsg* udp_msg_;
};
